#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
行为检测模块
作者: 元歌
开发地: 广州南沙铁建中心
时间: 2024年10月
"""

import numpy as np
from typing import List, Tuple, Dict, Optional
from collections import deque
import time

class ActionDetector:
    """行为识别器，用于检测用户的具体动作"""
    
    def __init__(self, history_size: int = 10):
        self.history_size = history_size
        self.pose_history = deque(maxlen=history_size)
        self.time_history = deque(maxlen=history_size)
        self.last_action = None
        self.action_start_time = None
        self.action_duration = 0
        
    def add_pose(self, landmarks, pose_info: Dict) -> Dict[str, any]:
        """
        添加新的姿态数据并检测行为
        
        Args:
            landmarks: 姿态关键点
            pose_info: 姿态信息
            
        Returns:
            行为检测结果
        """
        current_time = time.time()
        
        # 保存当前姿态和时间
        if landmarks:
            self.pose_history.append(landmarks)
            self.time_history.append(current_time)
        
        # 检测行为
        action_result = self._detect_actions(pose_info)
        
        return action_result
    
    def _detect_actions(self, pose_info: Dict) -> Dict[str, any]:
        """检测各种行为"""
        if len(self.pose_history) < 3:
            return {
                'action': 'unknown',
                'confidence': 0.0,
                'description': '数据不足',
                'details': {}
            }
        
        # 获取当前姿态
        current_pose = pose_info.get('pose', 'unknown')
        current_landmarks = self.pose_history[-1]
        
        # 检测各种行为
        actions = []
        
        # 1. 检测走路/跑步
        walking_result = self._detect_walking_running()
        if walking_result['confidence'] > 0.3:  # 降低阈值
            actions.append(walking_result)
        
        # 2. 检测挥手
        waving_result = self._detect_waving(current_landmarks)
        if waving_result['confidence'] > 0.3:  # 降低阈值
            actions.append(waving_result)
        
        # 3. 检测举手
        raising_hand_result = self._detect_raising_hand(current_landmarks)
        if raising_hand_result['confidence'] > 0.3:  # 降低阈值
            actions.append(raising_hand_result)
        
        # 4. 检测蹲下/起立
        squatting_result = self._detect_squatting_movement()
        if squatting_result['confidence'] > 0.3:  # 降低阈值
            actions.append(squatting_result)
        
        # 5. 检测转身
        turning_result = self._detect_turning()
        if turning_result['confidence'] > 0.3:  # 降低阈值
            actions.append(turning_result)
        
        # 6. 检测静止
        still_result = self._detect_still()
        if still_result['confidence'] > 0.2:  # 静止检测阈值更低
            actions.append(still_result)
        
        # 选择置信度最高的行为
        if actions:
            best_action = max(actions, key=lambda x: x['confidence'])
            return best_action
        else:
            return {
                'action': 'unknown',
                'confidence': 0.0,
                'description': '未检测到特定行为',
                'details': {}
            }
    
    def _detect_walking_running(self) -> Dict[str, any]:
        """检测走路或跑步"""
        if len(self.pose_history) < 5:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '数据不足'}
        
        # 分析腿部运动模式
        leg_movements = []
        for i in range(1, len(self.pose_history)):
            prev_landmarks = self.pose_history[i-1]
            curr_landmarks = self.pose_history[i]
            
            # 计算膝盖位置变化
            prev_left_knee = self._get_landmark_by_name(prev_landmarks, 'left_knee')
            curr_left_knee = self._get_landmark_by_name(curr_landmarks, 'left_knee')
            prev_right_knee = self._get_landmark_by_name(prev_landmarks, 'right_knee')
            curr_right_knee = self._get_landmark_by_name(curr_landmarks, 'right_knee')
            
            if all([prev_left_knee, curr_left_knee, prev_right_knee, curr_right_knee]):
                left_movement = abs(curr_left_knee[1] - prev_left_knee[1])
                right_movement = abs(curr_right_knee[1] - prev_right_knee[1])
                leg_movements.append((left_movement + right_movement) / 2)
        
        if not leg_movements:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '无法计算腿部运动'}
        
        # 计算平均运动幅度
        avg_movement = np.mean(leg_movements)
        movement_variance = np.var(leg_movements)
        
        # 判断是走路还是跑步
        if avg_movement > 0.05 and movement_variance > 0.001:
            if avg_movement > 0.08:
                return {
                    'action': 'running',
                    'confidence': min(0.9, avg_movement * 10),
                    'description': '正在跑步',
                    'details': {
                        'movement_intensity': avg_movement,
                        'movement_variance': movement_variance
                    }
                }
            else:
                return {
                    'action': 'walking',
                    'confidence': min(0.8, avg_movement * 8),
                    'description': '正在走路',
                    'details': {
                        'movement_intensity': avg_movement,
                        'movement_variance': movement_variance
                    }
                }
        
        return {'action': 'unknown', 'confidence': 0.0, 'description': '腿部运动不足'}
    
    def _detect_waving(self, landmarks) -> Dict[str, any]:
        """检测挥手动作"""
        if not landmarks:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '无关键点数据'}
        
        # 获取手部关键点
        left_wrist = self._get_landmark_by_name(landmarks, 'left_wrist')
        right_wrist = self._get_landmark_by_name(landmarks, 'right_wrist')
        left_shoulder = self._get_landmark_by_name(landmarks, 'left_shoulder')
        right_shoulder = self._get_landmark_by_name(landmarks, 'right_shoulder')
        
        if not all([left_wrist, right_wrist, left_shoulder, right_shoulder]):
            return {'action': 'unknown', 'confidence': 0.0, 'description': '缺少手部关键点'}
        
        # 计算手部相对于肩膀的位置
        left_hand_raised = left_wrist[1] < left_shoulder[1] - 0.1
        right_hand_raised = right_wrist[1] < right_shoulder[1] - 0.1
        
        # 计算手部水平位置（是否在身体侧面）
        left_hand_side = abs(left_wrist[0] - left_shoulder[0]) > 0.15
        right_hand_side = abs(right_wrist[0] - right_shoulder[0]) > 0.15
        
        # 检测挥手模式
        if left_hand_raised and left_hand_side:
            return {
                'action': 'waving_left',
                'confidence': 0.8,
                'description': '左手挥手',
                'details': {
                    'hand_position': 'left',
                    'hand_height': left_wrist[1] - left_shoulder[1]
                }
            }
        elif right_hand_raised and right_hand_side:
            return {
                'action': 'waving_right',
                'confidence': 0.8,
                'description': '右手挥手',
                'details': {
                    'hand_position': 'right',
                    'hand_height': right_wrist[1] - right_shoulder[1]
                }
            }
        
        return {'action': 'unknown', 'confidence': 0.0, 'description': '未检测到挥手动作'}
    
    def _detect_raising_hand(self, landmarks) -> Dict[str, any]:
        """检测举手动作"""
        if not landmarks:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '无关键点数据'}
        
        # 获取关键点
        left_wrist = self._get_landmark_by_name(landmarks, 'left_wrist')
        right_wrist = self._get_landmark_by_name(landmarks, 'right_wrist')
        left_shoulder = self._get_landmark_by_name(landmarks, 'left_shoulder')
        right_shoulder = self._get_landmark_by_name(landmarks, 'right_shoulder')
        nose = self._get_landmark_by_name(landmarks, 'nose')
        
        if not all([left_wrist, right_wrist, left_shoulder, right_shoulder, nose]):
            return {'action': 'unknown', 'confidence': 0.0, 'description': '缺少关键点数据'}
        
        # 计算手部高度（相对于头部）
        left_hand_height = left_wrist[1] - nose[1]
        right_hand_height = right_wrist[1] - nose[1]
        
        # 检测举手
        if left_hand_height < -0.1:  # 手在头部上方
            return {
                'action': 'raising_hand_left',
                'confidence': 0.9,
                'description': '左手举手',
                'details': {
                    'hand_position': 'left',
                    'hand_height': left_hand_height
                }
            }
        elif right_hand_height < -0.1:
            return {
                'action': 'raising_hand_right',
                'confidence': 0.9,
                'description': '右手举手',
                'details': {
                    'hand_position': 'right',
                    'hand_height': right_hand_height
                }
            }
        
        return {'action': 'unknown', 'confidence': 0.0, 'description': '未检测到举手动作'}
    
    def _detect_squatting_movement(self) -> Dict[str, any]:
        """检测蹲下/起立动作"""
        if len(self.pose_history) < 3:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '数据不足'}
        
        # 分析髋部高度变化
        hip_heights = []
        for landmarks in self.pose_history:
            left_hip = self._get_landmark_by_name(landmarks, 'left_hip')
            right_hip = self._get_landmark_by_name(landmarks, 'right_hip')
            
            if left_hip and right_hip:
                hip_height = (left_hip[1] + right_hip[1]) / 2
                hip_heights.append(hip_height)
        
        if len(hip_heights) < 3:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '无法计算髋部高度'}
        
        # 计算高度变化
        height_change = hip_heights[-1] - hip_heights[0]
        height_variance = np.var(hip_heights)
        
        if abs(height_change) > 0.05 and height_variance > 0.001:
            if height_change > 0:
                return {
                    'action': 'standing_up',
                    'confidence': min(0.8, abs(height_change) * 5),
                    'description': '正在起立',
                    'details': {
                        'height_change': height_change,
                        'movement_variance': height_variance
                    }
                }
            else:
                return {
                    'action': 'squatting_down',
                    'confidence': min(0.8, abs(height_change) * 5),
                    'description': '正在蹲下',
                    'details': {
                        'height_change': height_change,
                        'movement_variance': height_variance
                    }
                }
        
        return {'action': 'unknown', 'confidence': 0.0, 'description': '未检测到蹲起动作'}
    
    def _detect_turning(self) -> Dict[str, any]:
        """检测转身动作"""
        if len(self.pose_history) < 5:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '数据不足'}
        
        # 分析身体朝向变化
        shoulder_angles = []
        for landmarks in self.pose_history:
            left_shoulder = self._get_landmark_by_name(landmarks, 'left_shoulder')
            right_shoulder = self._get_landmark_by_name(landmarks, 'right_shoulder')
            
            if left_shoulder and right_shoulder:
                # 计算肩膀连线的角度
                angle = np.arctan2(right_shoulder[1] - left_shoulder[1], 
                                 right_shoulder[0] - left_shoulder[0])
                shoulder_angles.append(angle)
        
        if len(shoulder_angles) < 3:
            return {'action': 'unknown', 'confidence': 0.0, 'description': '无法计算身体朝向'}
        
        # 计算角度变化
        angle_change = shoulder_angles[-1] - shoulder_angles[0]
        angle_variance = np.var(shoulder_angles)
        
        if abs(angle_change) > 0.3 and angle_variance > 0.01:
            if angle_change > 0:
                return {
                    'action': 'turning_left',
                    'confidence': min(0.8, abs(angle_change) * 2),
                    'description': '向左转身',
                    'details': {
                        'angle_change': angle_change,
                        'angle_variance': angle_variance
                    }
                }
            else:
                return {
                    'action': 'turning_right',
                    'confidence': min(0.8, abs(angle_change) * 2),
                    'description': '向右转身',
                    'details': {
                        'angle_change': angle_change,
                        'angle_variance': angle_variance
                    }
                }
        
        return {'action': 'unknown', 'confidence': 0.0, 'description': '未检测到转身动作'}
    
    def _detect_still(self) -> Dict[str, any]:
        """检测静止状态"""
        if len(self.pose_history) < 3:  # 降低到3帧
            return {'action': 'unknown', 'confidence': 0.0, 'description': '数据不足'}
        
        # 分析整体运动
        total_movement = 0
        for i in range(1, len(self.pose_history)):
            prev_landmarks = self.pose_history[i-1]
            curr_landmarks = self.pose_history[i]
            
            # 计算关键点位置变化
            key_points = ['left_shoulder', 'right_shoulder', 'left_hip', 'right_hip', 'nose']
            for point_name in key_points:
                prev_point = self._get_landmark_by_name(prev_landmarks, point_name)
                curr_point = self._get_landmark_by_name(curr_landmarks, point_name)
                
                if prev_point and curr_point:
                    movement = np.sqrt((curr_point[0] - prev_point[0])**2 + 
                                     (curr_point[1] - prev_point[1])**2)
                    total_movement += movement
        
        avg_movement = total_movement / (len(self.pose_history) - 1) / len(key_points)
        
        if avg_movement < 0.02:  # 提高运动阈值，更容易检测到静止
            return {
                'action': 'stationary',  # 改为stationary以匹配中文显示
                'confidence': min(0.9, 1.0 - avg_movement * 50),  # 动态置信度
                'description': '保持静止',
                'details': {
                    'movement_level': avg_movement
                }
            }
        
        # 如果不是静止，则认为是移动
        return {
            'action': 'moving',
            'confidence': min(0.8, avg_movement * 20),  # 根据运动幅度计算置信度
            'description': '正在移动',
            'details': {
                'movement_level': avg_movement
            }
        }
    
    def _get_landmark_by_name(self, landmarks, landmark_name: str) -> Optional[Tuple[float, float, float]]:
        """根据名称获取关键点坐标"""
        if not landmarks:
            return None
        
        landmark_names = {
            'nose': 0,
            'left_eye_inner': 1, 'left_eye': 2, 'left_eye_outer': 3,
            'right_eye_inner': 4, 'right_eye': 5, 'right_eye_outer': 6,
            'left_ear': 7, 'right_ear': 8,
            'mouth_left': 9, 'mouth_right': 10,
            'left_shoulder': 11, 'right_shoulder': 12,
            'left_elbow': 13, 'right_elbow': 14,
            'left_wrist': 15, 'right_wrist': 16,
            'left_pinky': 17, 'right_pinky': 18,
            'left_index': 19, 'right_index': 20,
            'left_thumb': 21, 'right_thumb': 22,
            'left_hip': 23, 'right_hip': 24,
            'left_knee': 25, 'right_knee': 26,
            'left_ankle': 27, 'right_ankle': 28,
            'left_heel': 29, 'right_heel': 30,
            'left_foot_index': 31, 'right_foot_index': 32
        }
        
        if landmark_name not in landmark_names:
            return None
        
        landmark_idx = landmark_names[landmark_name]
        landmark = landmarks.landmark[landmark_idx]
        return (landmark.x, landmark.y, landmark.z)
    
    def get_action_chinese_name(self, action: str) -> str:
        """获取行为的中文名称"""
        chinese_names = {
            'walking': '正在走路',
            'running': '正在跑步',
            'waving_left': '左手挥手',
            'waving_right': '右手挥手',
            'raising_hand_left': '左手举手',
            'raising_hand_right': '右手举手',
            'squatting_down': '正在蹲下',
            'standing_up': '正在起立',
            'turning_left': '向左转身',
            'turning_right': '向右转身',
            'still': '保持静止',
            'unknown': '未知行为'
        }
        return chinese_names.get(action, '未知行为')
